package net.hangar5.xmlrpc; /* RpcServlet.java The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. The Original Code is "Hangar5 XMLRPC Library". The Initial Developer of the Original Code is James D. Rudnicki. Portions created by James D. Rudnicki are Copyright (C) 2001. All Rights Reserved. Contributor(s): */ import javax.servlet.*; import javax.servlet.http.*; import java.io.*; import net.hangar5.xmlrpc.*; //import com.dalsemi.system.TINIOS; /** * Base class for XML-RPC Servlets. * * This serlvet takes the XML-RPC data via an HTTP POST, executes * the call, and returns the response. To add methods to the servlet, * this class is subclassed and init is overriden. * * *

Usage Note: *

Depending on the servlet containter, on the client side you may need to * turn off keep-alive to use this servlet. * For example, in the Helma client : *

    XmlRpc.setKeepAlive( false );
* */ public class RpcServlet extends HttpServlet { /** Call dispatcher */ protected Dispatcher dispatcher; /** Count of calls executed */ protected long nCallCounter; private static final boolean debug =false; private static final boolean profile =false; /** GET handler. * XML-RPC does not use HTTP GET. This handler responds with a status * page about the dispatcher. */ public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.println("

RPC Server Stats

"); out.println("

Calls executed: "+ Long.toString(nCallCounter) +"

" ); out.close(); return; } /** POST handler. * This is the entry point for the XML-RPC call. */ public void doPost( HttpServletRequest request, HttpServletResponse response) throws ServletException { StringBuffer sbResp; StringBuffer sbCall; PrintWriter pwResp = null; long t1, t2; try { sbCall = readRequest( request ); if( profile ) { //t1 = TINIOS.uptimeMillis(); } /* execute call */ sbResp = dispatcher.executePost( "/", sbCall ); nCallCounter++; if( profile ) { //t2 = TINIOS.uptimeMillis(); //System.out.println( Long.toString( t2 - t1 ) ); //t1 = t2; } /* response header spec requires header to include length */ response.setContentType("text/xml"); response.setContentLength( sbResp.length()+1 ); /* response body */ pwResp = response.getWriter(); pwResp.write( sbResp.toString() ); pwResp.write( "\n" ); pwResp.flush(); pwResp.close(); if( profile ) { //t2 = TINIOS.uptimeMillis(); //System.out.println( Long.toString( t2 - t1 ) ); //t1 = t2; } } catch( IOException x ) { if( null != pwResp ) { pwResp.write( "imagine a useful message here" ); pwResp.flush(); } System.out.println( "Post failed:"+x.toString() ); } return; } /** Initialize the servlet with a dispatcher. *

This method constructs an empty dispatcher. * To have any behavior, subclass this class and * overide this method with one that adds more methods * to the dispatcher. *

example: *

   * public class MyRpcServlet extends net.hangar5.xmlrpc.RpcServlet {
   *   public void init(ServletConfig config)
   *     throws ServletException {
   *     super.init(config);
   *     IMethod m;
   *     m = (IMethod) new MyMethod();
   *     dispatcher.addMethod( m );
   *   }
   * }
   * 
*@see net.hangar5.xmlrpc.test.MyRpcServlet */ public void init(ServletConfig config) throws ServletException { super.init(config); dispatcher = new Dispatcher(); if( debug ) { System.out.println( "boot" ); dispatcher.setVerbose(debug); } nCallCounter = 0; return; } protected StringBuffer readRequest( HttpServletRequest request ) throws IOException { Reader rdRequest = (Reader)request.getReader(); /* The idea here is that it is pointless to call read at a rate much higher than the net IO speed of the TINI with the ethernet adapter. I'm trying to get fewer calls with larger numbers of bytes, as opposed to many calls with a few bytes. These numbers are rough guesses of about 50 kbyte / s. */ final int nSizeChunk = 250; final int nWaitBetweenChunks = 5; final int nTimeout = 20000; int nTotal; /* read entire request into a StringBuffer */ StringBuffer sbCall = new StringBuffer(1024); char[] cBuff = new char[nSizeChunk]; int n; nTotal = 0; do { if( rdRequest.ready() ) { n = rdRequest.read( cBuff, 0, nSizeChunk ); } else { n = -1; } if( n > 0 ) { sbCall.append( cBuff, 0, n ); if( debug ) { System.out.print( "@"+ n ); } try { nTotal += nWaitBetweenChunks; if( nTotal > nTimeout ) { if( debug ) { System.out.println( "timeout" ); System.out.println( sbCall.toString() ); System.out.println( sbCall.length() ); } throw new IOException(); } Thread.sleep( nWaitBetweenChunks ); } catch( InterruptedException x ) { } } } while( n >= 0 ); return sbCall; } } // end of class